﻿#include <cstdio>
#include <cstring>
#include <GL/glew.h>
#include <GL/freeglut.h>
#include "mat.h"
#include "glcamera.h"
#include "materials.h"
#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <ctime>
#include "sky.h"
#define BMP_Header_Length 54  //图像数据在内存块中的偏移量，百度知道，，，
using namespace std;

// 2d图时的线的类型
enum LINE_TYPE{
    LINE_HORIZONTAL = 1,
    LINE_VERTICAL
};

// 纹理ID
GLuint texMisaka;
GLuint texGround2;
GLuint texGround3;
GLuint texWall;
GLuint texWall2;
GLuint texGarden;
GLuint texSky;

//初始窗口长宽
int windowWidth = 1366;
int windowHeigh = 768;

// 迷宫类是给图像提供数据的，，，
// 天空类是天空，圆形的天空，，
Sky sky;
Mat maze;
// 控制球移动
bool moveable;

//路径数据
int currStep;
int totSteps;
int paths_color[500][3];

// 停止搜索路径展示
bool disabled_show;
// 停止行动
bool disabled_view_move;

// xuan转角，测试用
int Angle;

// 选项，待更新
int menuChoice;


//相机的初始参数，就是glulookAt的参数啦
Vector3d pos(-43.0, 3.4, 8.7);
Vector3d target(0.0, 3.40, 8.7);
Vector3d up(0.0, 1.0, 0.0);

//实例化相机
GLCamera *camera = new GLCamera(pos, target, up);
int postx, posty;

// 生成随机颜色
void color_step()
{
    srand(clock());
    for (int i = 0; i < 255; i++){
        paths_color[i][0] = rand()%256;
        paths_color[i][1] = rand()%256;
        paths_color[i][2] = rand()%256;
    }
}

//检查x是否为2的n次幂
int power_of_two(int x) {
    if (x <= 0) return 0;
    return (x & (x - 1)) == 0;
}

// 读取一个BMP文件作为纹理
// 返回纹理编号,要注意内存泄漏问题，纹理多了就有情况了
// from web
GLuint load_texture(const char* file_name)
{
    GLint width, height, total_bytes;
    GLubyte* pixels = 0;
    GLuint last_texture_ID=0, texture_ID = 0;

    // 打开文件，如果失败，返回
	FILE* pFile;
	fopen_s(&pFile, file_name, "rb");
    if( pFile == 0 )
        return 0;

    // 读取文件中图象的宽度和高度
    fseek(pFile, 0x0012, SEEK_SET);
    fread(&width, 4, 1, pFile);
    fread(&height, 4, 1, pFile);
    fseek(pFile, BMP_Header_Length, SEEK_SET);

    // 计算每行像素所占字节数，并根据此数据计算总像素字节数
    {
        GLint line_bytes = width * 3;
        while( line_bytes % 4 != 0 )
            ++line_bytes;
        total_bytes = line_bytes * height;
    }

    // 根据总像素字节数分配内存
    pixels = (GLubyte*)malloc(total_bytes);
    if( pixels == 0 )
    {
        fclose(pFile);
        return 0;
    }

    // 读取像素数据
    if( fread(pixels, total_bytes, 1, pFile) <= 0 )
    {
        free(pixels);
        fclose(pFile);
        return 0;
    }

    // 对就旧版本的兼容，如果图象的宽度和高度不是的整数次方，则需要进行缩放
    // 若图像宽高超过了OpenGL规定的最大值，也缩放
    {
        GLint max;
        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
        if( !power_of_two(width)
                || !power_of_two(height)
                || width > max
                || height > max )
        {
            const GLint new_width = 256;
            const GLint new_height = 256; // 规定缩放后新的大小为边长的正方形
            GLint new_line_bytes, new_total_bytes;
            GLubyte* new_pixels = 0;

            // 计算每行需要的字节数和总字节数
            new_line_bytes = new_width * 3;
            while( new_line_bytes % 4 != 0 )
                ++new_line_bytes;
            new_total_bytes = new_line_bytes * new_height;

            // 分配内存
            new_pixels = (GLubyte*)malloc(new_total_bytes);
            if( new_pixels == 0 )
            {
                free(pixels);
                fclose(pFile);
                return 0;
            }

            // 进行像素缩放
            gluScaleImage(GL_RGB,
                          width, height, GL_UNSIGNED_BYTE, pixels,
                          new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);

            // 释放原来的像素数据，把pixels指向新的像素数据，并重新设置width和height
            free(pixels);
            pixels = new_pixels;
            width = new_width;
            height = new_height;
        }
    }

    // 分配一个新的纹理编号
    glGenTextures(1, &texture_ID);
    if( texture_ID == 0 )
    {
        free(pixels);
        fclose(pFile);
        return 0;
    }

    // 绑定新的纹理，载入纹理并设置纹理参数
    // 在绑定前，先获得原来绑定的纹理编号，以便在最后进行恢复
    GLint lastTextureID=last_texture_ID;
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID);
    glBindTexture(GL_TEXTURE_2D, texture_ID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
                 GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
    glBindTexture(GL_TEXTURE_2D, lastTextureID);  //恢复之前的纹理绑定
    free(pixels);
    return texture_ID;
}

// 无聊的函数
// 加载光照材质
void loadMaterials(GLfloat ambient[], GLfloat diffuse[],
                   GLfloat specular[], GLfloat shininess[],
                   int type) {
    switch(type) {
    case 1:
        for (int i = 0; i < 13; i++){
            if (i < 4) {
                ambient[i] = Brass[i];
            }else if (i < 8) {
                diffuse[i-4] = Brass[i-4];
            }else if (i < 12){
                specular[i-8] = Brass[i-8];
            }else
                shininess[i-12] = Brass[i-12];
        }
        break;
    case 2:
        for (int i = 0; i < 13; i++){
            if (i < 4) {
                ambient[i] = Pearl[i];
            }else if (i < 8) {
                diffuse[i-4] = Pearl[i-4];
            }else if (i < 12){
                specular[i-8] = Pearl[i-8];
            }else
                shininess[i-12] = Pearl[i-12];
        }
        break;
    }
}

// 最初的测试地图第一步，画个地板, 为后面迷宫做准备
// from web modified.
void drawFloor() {

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    //glEnable(GL_LIGHTING);
    //GLfloat grey[] = {1, 0, 1, 0};

    glColor3f(0, 1, 0);
    glBegin(GL_LINES);
    glVertex3f(0, 0, 0);
    glVertex3f(0, 0, 120);
    glEnd();

    glBegin(GL_LINES);
    glVertex3f(0, 0, 0);
    glVertex3f(120, 0, 0);
    glEnd();

    glBegin(GL_LINES);
    glVertex3f(0, 0, 0);
    glVertex3f(0, 120, 0);
    glEnd();


    GLfloat light_ambient[] = { 1, 0, 0, 1.0 };
    GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_position0[] = { -100.0, 100.0, -100.0 ,0 };

    glLightfv(GL_LIGHT0, GL_AMBIENT , light_ambient ); //环境光
    glLightfv(GL_LIGHT0, GL_DIFFUSE , light_diffuse ); //漫射光
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); //镜面反射
    glLightfv(GL_LIGHT0, GL_POSITION, light_position0); //光照位置

    GLfloat mat_ambient[] = { 0.250000, 0.148000, 0.064750, 1.000000};
    GLfloat mat_diffuse[] = { 0.400000, 0.236800, 0.103600, 1.000000};
    GLfloat mat_specular[] = { 0.774597, 0.458561, 0.200621, 1.000000 };
    GLfloat mat_shininess[] = { 76.800003}; //材质RGBA镜面指数，数值在0～128范围内

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glEnable(GL_LIGHT0); //使第一盏灯有效

    glutSolidCube(1);
    int line, column;
    glPushMatrix();
    for (line = 0;line<120;line++) {
        glPushMatrix();
        for (column = 0;column<120;column++) {
            glColor3f(1.0, 0.0, 0.0);//红地板

            glBegin(GL_QUADS);//逆时针画出来的正方形才是正面朝着我们的
            glNormal3f(0, 1, 0);
            //glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, grey);
            glVertex3f(0.0, 0.0, 0.0);
            glVertex3f(1.0, 0.0, 0.0);
            glVertex3f(1.0, 0.0, 1.0);
            glVertex3f(0.0, 0.0, 1.0);
            glEnd();

            glTranslatef(1.0, 0.0, 0.0);
            glColor3f(1.0, 1.0, 1.0);//白地板
            glBegin(GL_QUADS);
            glNormal3f(0, 1, 0);
            //glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, grey);
            glVertex3f(0.0, 0.0, 0.0);
            glVertex3f(1.0, 0.0, 0.0);
            glVertex3f(1.0, 0.0, 1.0);
            glVertex3f(0.0, 0.0, 1.0);
            glEnd();
            glTranslatef(1.0, 0.0, 0.0);
        }
        glPopMatrix();//让原点回到每行初始位置(行最左侧)
        glTranslatef(0.0, 0.0, 2.0);
    }

    glPopMatrix();
    glTranslatef(0.0, 0.0, 1.0);
    glPushMatrix();
    for (line = 0;line<120;line++) {
        glPushMatrix();
        for (column = 0;column<120;column++) {

            glColor3f(1.0, 1.0, 1.0);//白地板
            glBegin(GL_QUADS);
            glNormal3f(0, 1, 0);
            //glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, grey);
            glVertex3f(0.0, 0.0, 0.0);
            glVertex3f(1.0, 0.0, 0.0);
            glVertex3f(1.0, 0.0, 1.0);
            glVertex3f(0.0, 0.0, 1.0);
            glEnd();

            glTranslatef(1.0, 0.0, 0.0);

            glColor3f(1.0, 0.0, 0.0);//红地板

            glBegin(GL_QUADS);
            glNormal3f(0, 1, 0);
            //glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, grey);
            glVertex3f(0.0, 0.0, 0.0);
            glVertex3f(1.0, 0.0, 0.0);
            glVertex3f(1.0, 0.0, 1.0);
            glVertex3f(0.0, 0.0, 1.0);
            glEnd();

            glTranslatef(1.0, 0.0, 0.0);
        }
        glPopMatrix();
        glTranslatef(0.0, 0.0, 2.0);
    }
    glPopMatrix();

    glPointSize (10);
    glColor3f(0, 1.0, 0);
    glBegin (GL_POINTS);
    glVertex3f(0, 2, 0);
    glEnd();

    glutSwapBuffers();
}

void init_light() {

    glEnable(GL_LIGHTING);

	// GL_SMOOTH模式时，由多边形的定点指定的颜色来过渡面的颜色
	// 在GL_FLAT模式下，整个图的颜色是任意一点的颜色
    glShadeModel(GL_SMOOTH);

    // 材质，不是很会用...,让物体表面呈现设置的特性
	// materials.h中有Nate Robin的光照材质
    GLfloat mat_ambient[] = { 0.250000, 0.148000, 0.064750, 1.000000 };
    GLfloat mat_diffuse[] = { 0.400000, 0.236800, 0.103600, 1.000000 };
    GLfloat mat_specular[] = { 0.774597, 0.458561, 0.200621, 1.000000 };
    GLfloat mat_shininess[] = { 76.800003}; //材质RGBA镜面指数，数值在0～128范围内
    loadMaterials(mat_ambient, mat_diffuse, mat_specular, mat_shininess, 2);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

    GLfloat light0_ambient[] = { 0.1, 0.1, 0.1, 1.0 };
    //GLfloat light1_ambient[] = {0.9098, 0.3647,0.0980,1};
    GLfloat light1_ambient[] = {1, 1,1,1};
    GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_position0[] = { 1000.0, 1000.0, 1000.0 ,1 };
    GLfloat light_position1[] = {-1000.0, 1000.0, -1000.0, 1};

	// 一个光照有多种组成，环境光，漫反射，镜面
    glLightfv(GL_LIGHT0, GL_AMBIENT , light0_ambient ); //环境光
    glLightfv(GL_LIGHT0, GL_DIFFUSE , light_diffuse ); //漫射光
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); //镜面反射
    glLightfv(GL_LIGHT0, GL_POSITION, light_position0); //光照位置

    glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient);
    glLightfv(GL_LIGHT1, GL_DIFFUSE , light_diffuse ); //漫射光
    glLightfv(GL_LIGHT1, GL_SPECULAR, light_specular); //镜面反射
    glLightfv(GL_LIGHT1, GL_POSITION, light_position1); //光照位置
    glEnable(GL_LIGHT1);
    glEnable(GL_LIGHT0);

    // 聚光灯效果测试
    //GLfloat light_position0[] = { 100.0, 10.0, 100.0 ,1 };
    //GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    //GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    //glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 70.0f);
    //glLightfv(GL_LIGHT0, GL_DIFFUSE , light_diffuse ); //漫射光
    //glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); //镜面反射
    //glLightfv(GL_LIGHT0, GL_POSITION, light_position0); //光照位置
    //glEnable(GL_LIGHT0);

     // 设置环境光
    GLfloat env[] = {0.1, 0.1, 0.1, 1};
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, env);
    // 光线追踪，可以使用物体自身的颜色
    glEnable(GL_COLOR_MATERIAL);
    // 物体的“前”面的材料特性
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);


    glEnable(GL_DEPTH_TEST);
}

void drawCube(double scale, GLuint texture) {

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture);

    const GLfloat x1 = -0.5*scale, x2 = 0.5*scale;
    const GLfloat y1 = -0.5*scale, y2 = 0.5*scale;
    const GLfloat z1 = -0.5*scale, z2 = 0.5*scale;

    int i, j;
    //指定六个面的四个顶点，每个顶点用3个坐标值表示
    //前 后 上 下 左 右

    GLfloat point[6][4][3] = {
        { { x1,y1,z1 },{ x1,y2,z1 },{ x2,y2,z1 },{ x2,y1,z1 } },
        { { x1,y1,z2 },{ x2,y1,z2 },{ x2,y2,z2 },{ x1,y2,z2 } },
        { { x1,y2,z1 },{ x1,y2,z2 },{ x2,y2,z2 },{ x2,y2,z1 } },
        { { x1,y1,z1 },{ x2,y1,z1 },{ x2,y1,z2 },{ x1,y1,z2 } },
        { { x2,y1,z1 },{ x2,y2,z1 },{ x2,y2,z2 },{ x2,y1,z2 } },
        { { x1,y1,z1 },{ x1,y1,z2 },{ x1,y2,z2 },{ x1,y2,z1 } },
    };
    // 六个面的贴图，二维贴图的图的左下角为(0, 0), 右上角是(1, 1)
    int dir[6][4][2] = {
        { { 0,0 },{ 0,1 },{ 1,1 },{ 1,0 } },
        { { 0,0 },{ 1,0 },{ 1,1 },{ 0,1 } },
        { { 0,1 },{ 0,0 },{ 1,0 },{ 1,1 } },
        { { 1,1 },{ 0,1 },{ 0,0 },{ 1,0 } },
        { { 1,0 },{ 1,1 },{ 0,1 },{ 0,0 } },
        { { 0,0 },{ 1,0 },{ 1,1 },{ 0,1 } },
    };
	// 为每个面指定贴图
    for (i = 0; i < 6; i++) {
        glBindTexture(GL_TEXTURE_2D, texture);
        glBegin(GL_QUADS);
        for (j = 0; j < 4; j++) {
            glTexCoord2iv(dir[i][j]);
            glVertex3fv(point[i][j]);
        }
        glEnd();
    }
    glDisable(GL_TEXTURE_2D);
}


//画六面而成的立体图
void drawCube(double scale) {
    //画单位立方体，好用于贴图

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texWall2);
    glPushMatrix();//!划重点!不写后果很严重（当然这个错误不是我犯的哈哈

    glBegin(GL_QUADS);
    glNormal3f(0.0f, 1.0f, 0.0f);//法向量，光照明要用的
    //up
    glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f*scale, 0.5f*scale, 0.5f*scale);
    glTexCoord2f(1.0f, 1.0f);glVertex3f(0.5f*scale, 0.5f*scale, -0.5f*scale);
    glTexCoord2f(0.0f, 1.0f);glVertex3f(-0.5f*scale, 0.5f*scale, -0.5f*scale);
    glTexCoord2f(1.0f, 0.0f);glVertex3f(-0.5f*scale, 0.5f*scale, 0.5f*scale);
    glEnd();

    glBegin(GL_QUADS);
    glNormal3f(0.0f, -1.0f, 0.0f);
    //down
    glTexCoord2f(0.0f, 1.0f); glVertex3f(0.5f*scale, -0.5f*scale, 0.5f*scale);
    glTexCoord2f(1.0f, 1.0f);glVertex3f(-0.5f*scale, -0.5f*scale, 0.5f*scale);
    glTexCoord2f(1.0f, 0.0f);glVertex3f(-0.5f*scale, -0.5f*scale, -0.5f*scale);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f*scale, -0.5f*scale, -0.5f*scale);
    glEnd();

    glBegin(GL_QUADS);
    glNormal3f(0.0f, 0.0f, 1.0f);
    //front
    glTexCoord2f(0.0f, 1.0f); glVertex3f(0.5f*scale, 0.5f*scale, 0.5f*scale);
    glTexCoord2f(1.0f, 1.0f);glVertex3f(-0.5f, 0.5f, 0.5f*scale);
    glTexCoord2f(1.0f, 0.0f);glVertex3f(-0.5f*scale, -0.5f*scale, 0.5f*scale);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f*scale, -0.5f*scale, 0.5f*scale);
    glEnd();

    glBegin(GL_QUADS);
    glNormal3f(0.0f, 0.0f, -1.0f);
    //back
    glTexCoord2f(0.0f, 0.0f); glVertex3f(0.5f*scale, 0.5f*scale, -0.5f*scale);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(0.5f*scale, -0.5f*scale, -0.5f*scale);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(-0.5f*scale, -0.5f*scale, -0.5f*scale);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.5f*scale, 0.5f*scale, -0.5f*scale);
    glEnd();

    glBegin(GL_QUADS);
    glNormal3f(-1.0f, 0.0f, 0.0f);
    //left
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f*scale, 0.5f*scale, 0.5f*scale);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f*scale, 0.5f*scale, -0.5f*scale);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(-0.5f*scale, -0.5f*scale, -0.5f*scale);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.5f*scale, -0.5f*scale, 0.5f*scale);
    glEnd();

    glBegin(GL_QUADS);
    glNormal3f(1.0f, 0.0f, 0.0f);
    //right
    glTexCoord2f(0.0f, 0.0f); glVertex3f(0.5f*scale, 0.5f*scale, 0.5f*scale);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(0.5f*scale, -0.5f*scale, 0.5f*scale);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f*scale, -0.5f*scale, -0.5f*scale);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f*scale, 0.5f*scale, -0.5f*scale);
    glEnd();

    glDisable(GL_TEXTURE_2D);
    glPopMatrix();//有Push当然要有Pop
}

void drawCross() {

	// 画定位的标记+
    glColor3f(1, 0, 0);
    glBegin(GL_LINES);
    glVertex2f(-0.5, 0.0);
    glVertex2f(0.5, 0);
    glEnd();
    glBegin(GL_LINES);
    glVertex2f(0, 0.5);
    glVertex2f(0, -0.5);
    glEnd();
}

void drawTrangle() {

	//拿原点作为下三角的尖
	glBegin(GL_TRIANGLES);
	glVertex2f(0, 1.732 / 3);
	glVertex2f(0.5, -1.732 / 6);
	glVertex2f(-0.5, -1.732 / 6);
	glEnd();
}

// 线
void drawLine(int type, int scale) {
    glBegin(GL_LINES);
    switch(type) {
    case 1:
        glVertex2f(-0.5*scale, 0.0);
        glVertex2f(0.5*scale, 0.0);
        break;
    case 2:
        glVertex2f(0, 0.5*scale);
        glVertex2f(0, -0.5*scale);
        break;
    default:
        break;
    }
    glEnd();
}

// 小地图
void draw_dungen_2d() {
    glClearColor(0.6509803921568628, 0.792156862745098, 0.9411764705882353, 1);
    for (int i = maze.n; i >= 1; i--) {
        for (int j = maze.m+1; j >= 1; j--) {
            if (i == 2 && j == 1) continue;//入口
            if (i == maze.n && j == maze.m+1 ) {
                glPushMatrix();
                glTranslatef(j * 1.8 + 5 , i * 1.8 + 5 - 1.8, 0);
                glColor3f(1, 0, 0);
                glScalef(2, 2, 0);
                drawTrangle();
                glPopMatrix();
                glColor3f(0, 1, 0);
                continue;//出口
            }
            if (i == 1 || i == maze.n || j == 1 || j == maze.m+1) {
                if (i == 1 || i == maze.n) {
                    glPushMatrix();
                    glTranslatef(j * 1.8 + 5, i * 1.8 + 5, 0);
                    glScalef(3.6, 1, 1);
                    drawLine(LINE_HORIZONTAL, 1);
                    glPopMatrix();
                } else {
                    glPushMatrix();
                    glTranslatef(j * 1.8 + 5, i * 1.8 + 5, 0);
                    glScalef(1, 3.6, 1);
                    drawLine(LINE_VERTICAL, 1);
                    glPopMatrix();
                }
                continue;
            }
            if (!maze.vis[i][j]) {
                if (i & 1) {
                    //fixed the bug
                    glPushMatrix();
                    if (maze.mat[i][j].east == 1 || maze.mat[i][j].west == 1) {
                        glTranslatef(j * 1.8 + 5, i * 1.8 + 5, 0);
                        glScalef(0, 1, 1);
                    } else {
                        glTranslatef(j * 1.8 + 5, i * 1.8 + 5, 0);
                        glScalef(3.6, 1, 1);
                    }
                    drawLine(LINE_HORIZONTAL, 1);
                    glPopMatrix();
                } else {
                    glPushMatrix();
                    glTranslatef(j * 1.8 + 5, i * 1.8 + 5, 0);
                    glScalef(1, 3.6, 1);
                    drawLine(LINE_VERTICAL, 1);
                    glPopMatrix();
                }
            } else {
                // 空的地方
            }
        }
    }
}

// glutDisplayFunc()的回调函数
void drawDungen() {
	//清除颜色，具体流程为，glClearColor指定要设置的颜色
	// glClear执行
    glClearColor(1, 1, 1, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// 两个viewport
    for (int loop = 0; loop < 2; loop++) {
		// 3d地图层
        if( loop == 0) {
            glViewport (0, 0, windowWidth, windowHeigh);
            glMatrixMode(GL_PROJECTION); //
            glLoadIdentity();
            camera->setShape(60.0, (GLfloat)windowWidth/(GLfloat)windowHeigh, 0.1, 50000.0);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            camera->setModelViewMatrix();
            //debug info
            std::cout << camera->get_pos() << std::endl;

            //天空
            sky.ShowSky();

            // 绘制入口的一张图片
            glEnable(GL_TEXTURE_2D);
            glBindTexture(GL_TEXTURE_2D, texMisaka);
            glBegin(GL_QUADS);
            glTexCoord2f(0.0f, 0.0f); glVertex3f(-2.0f, .0f, 16.0f);
            glTexCoord2f(0.0f, 1.0f); glVertex3f(-2.0f, 8.0f, 16.0f);
            glTexCoord2f(1.0f, 1.0f); glVertex3f(-2.0f, 8.0f, 8.0f);
            glTexCoord2f(1.0f, 0.0f); glVertex3f(-2.0f, .0f, 8.0f);
            glEnd();
            glDisable(GL_TEXTURE_2D);

            //划线定位
            glColor3f(0, 0, 0);
            glutSolidSphere(0.1, 5, 5);

            glColor3f(0, 0, 1);
            glBegin(GL_LINES);
            glVertex3f(0, 0, 0);
            glVertex3f(0, 0, 1200);
            glEnd();

            glBegin(GL_LINES);
            glColor3f(1, 0, 0);
            glVertex3f(0, 0, 0);
            glVertex3f(1200, 0, 0);
            glEnd();

            glBegin(GL_LINES);
            glColor3f(0, 1, 0);
            glVertex3f(0, 0, 0);
            glVertex3f(0, 1200, 0);
            glEnd();

            //地板
            glEnable(GL_TEXTURE_2D);
            glBindTexture(GL_TEXTURE_2D, texGround3);
            glBegin(GL_QUADS);
            glNormal3f(0, 1, 0);//设置法线，使其受观照影响
            //y轴值由下面的glScalef的y值确定，使其平平的
            glTexCoord2f(0.0f, 0.0f); glVertex3f(12000, -14, 12000);
            glTexCoord2f(0.0f, 1.0f); glVertex3f(12000, -14, -12000);
            glTexCoord2f(1.0f, 1.0f); glVertex3f(-12000, -14, -12000);
            glTexCoord2f(1.0f, 0.0f); glVertex3f(-12000, -14, 12000);
            glEnd();
            glDisable(GL_TEXTURE_2D);

            //定位用的大砖块
            glPushMatrix();
            glTranslatef(-10, 0, 0);
            glScalef(2, 2, 1);
            glColor4f(0.5803921, .0, 0.82745098, 0.0);
            drawCube(1);
            glPopMatrix();

            glPushMatrix();
            glTranslatef(-15, 0, 0);
            glScalef(2, 2, 1);
            glColor4f(1, 0, 0, 1);
            drawCube(2);
            glPopMatrix();
            // 原生，无贴图的
            glPushMatrix();
            glTranslatef(-20, 0, 0);
            glScalef(2, 2, 1);
            glColor4f(0, 0, 1, 1);
            glutSolidCube(2);
            glPopMatrix();

            // dungen
            for (int i = 1; i <= maze.n; i++) {
                for (int j = 1; j <= maze.m+1; j++) {

                    if (i == 2 && j == 1) continue;//入口
                    if (i == maze.n && j == maze.m ) continue;
                    if (!maze.vis[i][j]) {
                        if (i & 1) {
                            // 打横的墙
                            int offset = 6;// base;
                            glColor4f(0, .2, 0, 0);
                            glPushMatrix();
                            if (maze.mat[i][j].east == 1) {
                                offset += 6;
                            }
                            glTranslatef(j * 12 - offset, 0, i * 12 - 12);
                            // adjust.
							// 横向误差调整：
							// 如果右方的没有(.east = 0)则稍调整
                            if (maze.mat[i][j].east == 1) {
                                glScalef(0.25, 12, 0.375);
                            } else {
                                glScalef(12 / 4 * 1, 12, 0.25);
                            }
                            drawCube(4, texWall2);
                            glPopMatrix();
                        } else {
                            // 竖
                            glColor4f(.0, .2, .0, .3);
                            glPushMatrix();
							glTranslatef(j * 12 - 12, 0, i * 12 - 12);
                            glScalef(0.25, 12, 12 / 2);
                            drawCube(4, texWall2);
                            glPopMatrix();
                        }
                    } else {
                        // 空的空间，可以添加一些点布置迷宫物品
                        if ((i == maze.er && j == maze.ec) || moveable) {
                            glColor4f(1.0, .0, 0, 0.3);
                            glPushMatrix();
                            if (!moveable) {
                                glTranslatef(j * 12 - 12, 4, i * 12 - 12);
                            }
                            else {
                                int offsetx;
                                int offsety;
                                if (menuChoice == 0) {
                                    offsetx = maze.path[currStep].first;
                                    offsety = maze.path[currStep].second;
                                }else{
                                    offsetx = maze.path_bfs[currStep].first;
                                    offsety = maze.path_bfs[currStep].second;
                                }
                                glTranslatef(offsety * 12 - 12, 4, offsetx * 12 - 12);
                            }
                            glutSolidSphere(2, 20, 20);
                            glPopMatrix();

							// 显示轨迹用
                            if (!disabled_show) {
                                for (int ii = 0; ii < currStep; ii++) {
                                    glPushMatrix();
                                    int xx, yy;
                                    if (menuChoice == 0) {
                                        xx = maze.path[ii].first;
                                        yy = maze.path[ii].second;
                                    }else{
                                        xx = maze.path_bfs[ii].first;
                                        yy = maze.path_bfs[ii].second;
                                    }
                                    glTranslatef(yy * 12 - 12 , 4, xx * 12 - 12);
                                    glColor3f(paths_color[ii%255][0],
                                            paths_color[ii%255][1],
                                            paths_color[ii%255][2]);

                                    glutSolidSphere(2, 2, 3);
                                    glPopMatrix();
                                }
                            }
                        }
                    }
                }
            }
        }
		// 2d地图层（直接绘制在表面上，所以顺序有别）
        if (loop == 1) {
            glViewport(0, windowWidth/4, windowWidth/4, (GLsizei)windowHeigh);
            glMatrixMode(GL_PROJECTION); //
            glLoadIdentity();
            gluOrtho2D(0, 100, 0, 100);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            //这时候的平移要注意,此时为2为坐标，超过了Ortho的设置会不见
            glPushMatrix();

			// 调整位置
            glTranslatef(0, -5, 0);
            glColor3f(0, 1, 0);
            draw_dungen_2d();
            glPopMatrix();

            // 画人的坐标（用“+”表示），要作映射转换
			// 转换：在3d地图中的路宽为48那么中间就是24（将在路中间显示，就为24）
			// 系数：由2d地图自身路宽决定，需调教的
            glPushMatrix();
            glTranslatef(3.6 * camera->get_pos().x() / 24 + 1.8 * 4, 1.8 * 1 + 3.6 * camera->get_pos().z() / 24,0);
            drawCross();
            glPopMatrix();

			glPushMatrix();
            glRotatef(Angle, 0, 0, 1.0);
            glColor4f(1.0, 1.0, 0.0, 0.4);
            drawTrangle();
			glPopMatrix();
        }
    }

    glutSwapBuffers();
}

void init() {
    //glClearColor(0.5803921, .0, 0.82745098, 0.0);
    // 设置背景色，蓝色或黑色
    glClearColor(0.5294111, 0.80784, 0.9215686, 0.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    postx = 0, posty = 0;
    menuChoice = 0;
    disabled_show = false;
    color_step();
    moveable = false;
	disabled_view_move = false;
    currStep = 0;
    maze.generate();
    maze.preprocess();
    //预先载入纹理
    texMisaka = load_texture("../dungen_map/p3.bmp");
    texGarden = load_texture("../dungen_map/pic/p2.bmp");
    texWall = load_texture("../dungen_map/pic/wall.bmp");
    texWall2 = load_texture("../dungen_map/wall2.bmp");
    texGround2 = load_texture("../dungen_map/ground2.bmp");
    texGround3 = load_texture("../dungen_map/ground3.bmp");
    texSky = load_texture("../dungen_map/sky1.bmp");
    sky.InitSky(0, 9000, 0, 10000, texMisaka);

}

void reshape(int width, int heigh) {
    windowHeigh = heigh;
    windowWidth = width;
	// 重置窗口大小时要更改viewport等信息
    for (int loop = 0; loop < 2; loop++) {
        if (loop == 0) {
            glViewport (0, 0, windowWidth, windowHeigh);
            glMatrixMode(GL_PROJECTION); //
            glLoadIdentity();
            camera->setShape(60.0, (GLfloat)windowWidth/(GLfloat)windowHeigh, 0.1, 50000.0);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            camera->setModelViewMatrix();
        }
        if (loop == 1) {
            glViewport(width/4*3, 0, windowWidth/4, (GLsizei)windowHeigh);
            glMatrixMode(GL_PROJECTION); //
            glLoadIdentity();
            gluOrtho2D(0, 100, 0, 100);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
        }
    }
}

void do_something() {

	// 调试数据
    maze.print_line();
}


// 旋转x分量
void RotateX(float angle){
	if (disabled_view_move)
		return;
    float d = camera->getDist();
    int cnt = 120;
    float theta = angle / cnt;
    float slide_d = -2 * d * sin(theta*3.1415915 / 360);
    camera->yaw(theta/2);

    for(; cnt != 0; cnt--) {
        //camera->slide(slide_d, 0, 0);
        camera->yaw(theta);
    }
    camera->yaw(-theta / 2);
}

// 旋转y分量
void RotateY(float angle) {
	if (disabled_view_move)
		return;
    float d = camera->getDist();
    int cnt = 120;
    float theta = angle / cnt;
    float slide_d = 2 * d * sin(theta*3.1415915 / 360);
    camera->pitch(theta/2);

    for(; cnt != 0; cnt--) {
        //camera->slide(0, slide_d, 0);
        camera->pitch(theta);
    }
    camera->pitch(-theta / 2);
}

// 按键过滤函数
void cameraMove(char key) {

	if (disabled_view_move)
		return;
	switch (key) {
		case 'w':
			camera->slide(0, 0, -1.50);
			break;
		case 's':
			camera->slide(0, 0, 0.30);
			break;
		case 'd':
			camera->slide(0.30, 0, 0);
			break;
		case 'a':
			camera->slide(-0.30, 0, 0);
			break;
		case 'r':
			camera->slide(0, -0.30, 0);
			break;
		case 'q':
			camera->slide(0, 0.30, 0);
			break;
		case 'z':
			camera->yaw(10);
			break;
		case 'x':
			camera->yaw(-10);
			break;
		case 'e':
			camera->roll(10);
			break;
		case 'f':
			camera->roll(-10);
			break;
		defalut:
			break;
	}
}

// 在鼠标移动的过程，并且，一定有按键的发生时的回调函数
void mousemotion(int x, int y) {
}

// 在鼠标移动的过程（没有按键的发生）时的回调函数
// 直接视角变换
void passivemouse(int x, int y){
    int dx = x - postx;
    int dy = y - posty;
    if (fabs(dx) > fabs(dy))
        RotateX(dx);
    else
        RotateY(dy);
    glutPostRedisplay();

    postx = x;
    posty = y;

}

// 鼠标按下是的响应事件设置
void mouse(int button, int status, int x, int y) {

    postx = x;
    posty = y;

    switch(button) {
    case GLUT_RIGHT_BUTTON:
        if (status == GLUT_DOWN)
            break;
    default:
        break;
    }
    glutPostRedisplay();
}

void make_it_move() {
    currStep += 1;
}

// 计时回调函数，
// glutTimeFunc(int, void(*)(int), value)的参数：
//  millionsecs, 回调函数，计时器的编号
void timer(int x) {
    currStep += 1;
    if (currStep > totSteps)
        return ;
    glutPostRedisplay();
    glutTimerFunc(200, timer, 1);
}

// 键盘事件
void keyboard(unsigned char key, int x, int y) {
    switch (key) {
    case 27:
        exit(0);
        break;
	case 'c':
		disabled_view_move = !disabled_view_move;
		break;
    case 'v':
        disabled_show = true;
        break;
    case 'b':
        menuChoice = !menuChoice;
        totSteps = maze.path_bfs.size();
        break;
    case 'o':
        glutTimerFunc(1000, timer, 1);
        break;
    case 'u':
        currStep = 0;
        moveable = true;
        if (menuChoice == 0)
            totSteps = maze.totSteps;
        else if (menuChoice == 1)
            totSteps = maze.path_bfs.size();
        break;
    case 'l':
        if (currStep == totSteps)
            break;
        make_it_move();
        glutPostRedisplay();
        break;
    case 'y':
        init();
        init_light();
        break;
    case 'g':
        do_something();
        glutPostRedisplay();
        break;
	case 'w':
	case 's':
	case 'd':
	case 'a':
	case 'r':
	case 'q':
	case 'z':
	case 'x':
	case 'e':
	case 'f':
		cameraMove(key);
		glutPostRedisplay();
		break;
    case 'm':
        Angle += 60;
        Angle %= 360;
        glutPostRedisplay();
        break;
    default:
        break;
    }
}

int main(int argc, char *argv[]) {
    srand(clock());
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB |GLUT_DEPTH);
    glutInitWindowSize(1366, 768);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("floor");
    init();
    init_light ();
	// 测试
    //glutDisplayFunc(drawFloor);
    glutDisplayFunc(drawDungen);
    glutReshapeFunc(reshape);
    glutKeyboardFunc (keyboard);

    glutMouseFunc(mouse);
    glutMotionFunc(mousemotion);
    glutPassiveMotionFunc(passivemouse);
    glutMainLoop();

    return 0;
}
